package Juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在，
 * 1.volatile  是Java虚拟机提供的轻量级的同步机制
 *     1.1：保证可见性     :   所有线程有自己的工作内存  操作main内存时 先复制再操作 再写入  通知其他线程
 *     1.2：不保证原子性   :   先复制再操作 再写入 中间有时间差 可能会有别的线程操作过了
 *     1.3：不允许重排列   :   在volatile变量进行写操作时加入store屏障指令   StoreStore  volatile写       StoreLoad
 *                          在volatile变量进行读操作时加入Load屏障指令      volatile读   LoadLoad      LoadStore
 *
 * 2.JMM   java内存模型//一种规范
 *    2.1：可见性        线程结束之前 ，必须刷新到主内存
 *    2.2：原子性        线程加锁之前，必须重新读取主内存到自己的工作内存
 *
 *    2.3：有序性
 *
 *   JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在，
 *   它描述的是一组规则或规范，通过这组规范
 *   定义了程序中各个变量(包括实例字段，静态字段和构成数组对象的元素)的访问方式。
 *   由于JVM运行程序的实体是线程，而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间)，
 *   工作内存是每个线程的私有数据区域，而Java内 存模型中规定所有变量都存储在主内存，
 *   主内存是共享内存区域，所有线程都可以访问，但线程对变量的操作(读取赋值等)必须在工作内存中进行，
 *   首先要将变量从主内存拷贝到的线程自己的工作内存空间，然后对变量进行操作，操作完成后再将变量写回主内存，
 *   不能直接操作主内存中的变量，各个线程中的工作内存中存储着主内存中的变量副本拷贝，
 *   因此不同的线程间无法访问对方的工作内存，线程间的通信(传值)必须通过主内存来完成，其简要访问过程如下图:
 */

class mydata {//MyData.java->MyData.class->jvm字节码
     int number=0;
    public void add(){this.number=60;}
    public  void addplus(){ ++number;}
    AtomicInteger atomicInteger=new AtomicInteger();//原子性的int
    public  void atomic(){atomicInteger.getAndIncrement();}//i++
    //凭啥能不加锁实现原子性   它使用unsafe（系统原语  不可能被打断） 也用到了CAS（比较并交换）
}

class ReSortSeqDemo{
     int a=0;
//    AtomicInteger a=new AtomicInteger(0);//原子性的int
    boolean flag =false;
    public void me1(){
//        a.compareAndSet(a.get(),1);
        a=1;
        flag=true;
    }
    public void me2(){
        if(flag){
            a+=5;
//            a.getAndAdd(5);
            if(a!=6)System.out.println(a);//在多线程下面   a可能等于11 也可能等于1 也可能输出6
            flag=false;
        }
    }
}

public class JMM {    //java内存模型  //主类

    public static void main(String[] args) {
        volatile_sort_demo();
    }

    private static volatile JMM instance = null; //加volatile禁止指令重排
    private JMM() {
        System.out.println(Thread.currentThread().getName() + "\t我是构造方法");
    }
    public static JMM getInstance() {
        if(instance == null){
            synchronized (JMM.class){   //锁加在这里  在多线程下有可能指令重排 会先执行return
                if(instance == null)//实现单例模式
                instance = new JMM();
            }
        }
        return instance;
    }
    private static void 构造函数_的多线程应用() {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                System.out.println(JMM.getInstance());
            },String.valueOf(i+1)).start();
        }
        System.out.println(JMM.getInstance() == JMM.getInstance());
        System.out.println(JMM.getInstance() == JMM.getInstance());
        System.out.println(JMM.getInstance() == JMM.getInstance());


    }

    private static void volatile_sort_demo() {
//        int x=11;   //1
//        int y=12;   //2
//        y+=x*x;      //3
//        x=x+5;      //4
//        //有可能1234     1243   21**
//        System.out.println(x+"       "+y);


        ReSortSeqDemo reSortSeqDemo=new ReSortSeqDemo();
        for (int i = 0; i < 10000; i++) {

            new Thread(()->{

                reSortSeqDemo.me2();
            },String.valueOf(i+1)).start();
            new Thread(()->{
                reSortSeqDemo.me1();
            },String.valueOf(i+1)).start();
        }
    }

    private static void AtomDemo() {
        mydata my =new mydata();

        for (int i = 0; i < 20; i++) {
            new Thread(() ->{
                for (int j = 1; j <= 1000; j++) {
                    my.addplus();my.atomic();
                }
            },String.valueOf(i)).start();
        }

        while (Thread.activeCount()>2){ Thread.yield();}//线程个数

        System.out.println(Thread.currentThread().getName()+"\t        int     "+my.number);

        System.out.println(Thread.currentThread().getName()+"\t AtomicInteger  "+my.atomicInteger);
    }        //volatile  不保证原子性

    private static void volatile_lookx()  {
        mydata my=new mydata();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try{TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){ e.printStackTrace();}
            my.add();
            System.out.println(Thread.currentThread().getName()+"\t come in"+my.number);
        },"A").start();

        while (my.number==0){//他会保存以前的0   一直死循环    加volatile就会通知  它失效了
//            try{TimeUnit.MILLISECONDS.sleep(100);}catch (InterruptedException e){ e.printStackTrace();}
            //System.out.print(my.number);
//            System.out.print("");//不加volatile  在循环体里加一些东西   也可以让my.number获取新的值  间接保证原子性
//        for   do{}while()  循环同理
            /**
             * while循环体为空为什么不会退出循环，加入一句打印就会退出循环？
             * 加入打印，循环应该也不是正常退出，此时是什么机制退出循环的？
             *
             * 因为多线程之间不是绝对同步的。
             * 在运行时为了提高效率会将数据加载到寄存器中，所以有时虽然内存中数据已经改变，但还未即时更新到寄存器中，就会出现不同步的情况。
             * 至于为什么空执行体迟迟不与内存数据同步，我猜测是访问flag频率过高，jvm的调度机制可能会选择在执行其他任务的时候同步flag，
             * 所以当你加了一句打印语句之后发现flag更新了。所以你每次得到的递增值也是不同的。
             * volatile 关键字可以强制每次都从内存中读取，这样可以实现同步变量，但效率会低不少。
             */
        }
        //for (;my.number==0;){System.out.print("");}
//        do{System.out.print("");}while (my.number==0);
        System.out.println(Thread.currentThread().getName()+"\t over   "+my.number);
    }  //volatile  保证可见性

}
